package org.apache.sad.core.framework.logging.log4j;

import java.io.File;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.Writer;

import org.apache.log4j.Layout;
import org.apache.log4j.RollingFileAppender;
import org.apache.log4j.helpers.CountingQuietWriter;
import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.spi.LoggingEvent;


public class ConfigurableRollingFileAppender extends RollingFileAppender {
	private AppenderConfiguration appenderConfiguration = AppenderConfiguration.getInstance();

	private String fileExtension;

	protected long nextRollover;

	private int percentageLogging;
	
	private boolean logRequestResponseSize;
	
	public ConfigurableRollingFileAppender() {
		super();
	}


	public ConfigurableRollingFileAppender(Layout layout, String filename)
			throws IOException {
		super(layout, filename);
	}


	public ConfigurableRollingFileAppender(Layout layout, String filename,
			boolean append) throws IOException {
		super(layout, filename, append);
	}

	protected String getCompleteFileName(int index) {
		return (fileExtension != null && !fileExtension.trim().equalsIgnoreCase("")) ? removeFileExtensionFromName(fileName)+ '.' + index + '.' + fileExtension : fileName + '.' + index;
	}

	public String getFileExtension() {
		return fileExtension;
	}

	public long getNextRollover() {
		return nextRollover;
	}

	public int getPercentageLogging() {
		return percentageLogging;
	}

	protected String removeFileExtensionFromName(String fileName) {
		return fileName.substring(0, fileName.lastIndexOf("."));
	}

	@Override
	public void rollOver() {
		File target;
		File file;
		if (qw != null) {
			long size = ((CountingQuietWriter) qw).getCount();
			LogLog.debug("rolling over count=" + size);

			nextRollover = size + maxFileSize;
		}
		LogLog.debug("maxBackupIndex=" + maxBackupIndex);
		boolean renameSucceeded = true;
		// If maxBackups <= 0, then there is no file renaming to be done.
		if (maxBackupIndex > 0) {
			// Delete the oldest file, to keep Windows happy.
			file = new File(getCompleteFileName(maxBackupIndex));
			if (file.exists())
				renameSucceeded = file.delete();
			// Map {(maxBackupIndex - 1), ..., 2, 1} to {maxBackupIndex, ..., 3,
			// 2}
			for (int i = maxBackupIndex - 1; i >= 1 && renameSucceeded; i--) {
				file = new File(getCompleteFileName(i));
				if (file.exists()) {
					target = new File(getCompleteFileName(i + 1));
					LogLog.debug("Renaming file " + file + " to " + target);
					renameSucceeded = file.renameTo(target);
				}
			}
			if (renameSucceeded) {
				// Rename fileName to fileName.1
				target = new File(getCompleteFileName(1));
				this.closeFile(); // keep windows happy.
				file = new File(fileName);
				LogLog.debug("Renaming file " + file + " to " + target);
				renameSucceeded = file.renameTo(target);
				//
				// if file rename failed, reopen file with append = true
				//
				if (!renameSucceeded) {
					try {
						this.setFile(fileName, true, bufferedIO, bufferSize);
					} catch (IOException e) {
						LogLog.error("setFile(" + fileName
								+ ", true) call failed.", e);
					}
				}
			}
		}
		//
		// if all renames were successful, then
		//
		if (renameSucceeded) {
			try {
				// This will also close the file. This is OK since multiple
				// close operations are safe.
				this.setFile(fileName, false, bufferedIO, bufferSize);
				nextRollover = 0;
			} catch (IOException e) {
				LogLog.error("setFile(" + fileName + ", false) call failed.",
								e);
			}
		}
	}

	/**
	 * Sets the {@link #fileExtension}.
	 * 
	 * @param fileExtension
	 *            The {@link #fileExtension}.
	 */
	public void setFileExtension(String fileExtension) {
		this.fileExtension = fileExtension;
	}

	/**
	 * Sets the {@link #nextRollover}.
	 * 
	 * @param nextRollover
	 *            The {@link #nextRollover}.
	 */
	public void setNextRollover(long nextRollover) {
		this.nextRollover = nextRollover;
	}

	/**
	 * Sets the {@link #percentageLogging}.
	 * 
	 * @param percentageLogging
	 *            The {@link #percentageLogging}.
	 */
	public void setPercentageLogging(int percentageLogging) {
		this.percentageLogging = percentageLogging;

		// Set it in the appender configuration as well
		this.appenderConfiguration.setPercentageLogging(percentageLogging);
	}

	/**
	 * This method differentiates RollingFileAppender from its super class. Copy
	 * paste from RollingFileAppender, as the nextRollover attribute is private
	 * in RollingFileAppender, and had to be included here.
	 */
	protected void subAppend(LoggingEvent event) {
		super.subAppend(event);
		if (fileName != null && qw != null) {
			long size = ((CountingQuietWriter) qw).getCount();
			if (size >= maxFileSize && size >= nextRollover) {
				rollOver();
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.apache.log4j.WriterAppender#writeHeader()
	 */
	@Override
	protected void writeHeader() {
		// Get the log file
		File f = new File(getFile());

		/*
		 * Check if the log file does not already exist, and if so, then write
		 * out the header. Otherwise, do nothing.
		 */
		if (f.length() == 0) {
			super.writeHeader();
		}
	}

	/* (non-Javadoc)
	 * @see org.apache.log4j.RollingFileAppender#setFile(java.lang.String, boolean, boolean, int)
	 */
	@Override
	public synchronized void setFile(String fileName, boolean append, boolean bufferedIO, int bufferSize) 
	throws IOException {		
	    LogLog.debug("setFile called: " + fileName + ", " + append);

	    if (bufferedIO) {
	      super.setImmediateFlush(false);
	    }

	    reset();
	    FileOutputStream ostream = null;
	    try
	    {
	      ostream = new FileOutputStream(fileName, append);
	    }
	    catch (FileNotFoundException ex)
	    {
	      String parentName = new File(fileName).getParent();
	      if (parentName != null) {
	        File parentDir = new File(parentName);
	        if ((!(parentDir.exists())) && (parentDir.mkdirs()))
	          ostream = new FileOutputStream(fileName, append);
	        else
	          throw ex;
	      }
	      else {
	        throw ex;
	      }
	    }
	    Writer fw = super.createWriter(ostream);
	    if (bufferedIO)
	      fw = new AuditLoggerWriter(fw, bufferSize);

	    setQWForFiles(fw);
	    this.fileName = fileName;
	    this.fileAppend = append;
	    this.bufferedIO = bufferedIO;
	    this.bufferSize = bufferSize;
	    this.writeHeader();
	    
	    if (append) {
	        File f = new File(fileName);
	        ((CountingQuietWriter)this.qw).setCount(f.length());
	    }
	    LogLog.debug("setFile ended");	    
	}

	public boolean isLogRequestResponseSize() {
		return logRequestResponseSize;
	}

	public void setLogRequestResponseSize(boolean logRequestResponseSize) {
		this.logRequestResponseSize = logRequestResponseSize;
		this.appenderConfiguration.setLogRequestResponseSize(logRequestResponseSize);
	}

		
}